package com.atguigu.tingshu.search.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.extra.pinyin.PinyinUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import co.elastic.clients.elasticsearch._types.aggregations.LongTermsBucket;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.CompletionSuggestOption;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
import co.elastic.clients.elasticsearch.core.search.Suggestion;
import co.elastic.clients.json.JsonData;
import com.alibaba.nacos.common.utils.StringUtils;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.thread.ThreadPoolConfig;
import com.atguigu.tingshu.model.album.AlbumAttributeValue;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.BaseCategory3;
import com.atguigu.tingshu.model.album.BaseCategoryView;
import com.atguigu.tingshu.model.search.AlbumInfoIndex;
import com.atguigu.tingshu.model.search.AttributeValueIndex;
import com.atguigu.tingshu.model.search.SuggestIndex;
import com.atguigu.tingshu.model.user.UserInfo;
import com.atguigu.tingshu.query.search.AlbumIndexQuery;
import com.atguigu.tingshu.repository.AlbumInfoIndexRepository;
import com.atguigu.tingshu.repository.SuggestIndexRepository;
import com.atguigu.tingshu.search.service.SearchService;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexVo;
import com.atguigu.tingshu.vo.search.AlbumSearchResponseVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import io.netty.util.concurrent.CompleteFuture;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.asm.Advice;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.elasticsearch.core.suggest.Completion;
import org.springframework.data.elasticsearch.core.suggest.response.Suggest;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;


@Slf4j
@Service
@SuppressWarnings({"all"})
public class SearchServiceImpl implements SearchService {
    private final String ALBUM_INFO = "albuminfo";
    private static final String SUGGEST = "suggestinfo";
    @Autowired
    private UserFeignClient userFeignClient;
    @Autowired
    private AlbumFeignClient albumFeignClient;
    @Autowired
    private ThreadPoolExecutor executor;
    @Autowired
    private AlbumInfoIndexRepository repository;
    @Autowired
    private SuggestIndexRepository suggestIndexRepository;
    @Autowired
    private ElasticsearchClient elasticsearchClient;

    /**
     * 上架专辑
     *
     * @param albumId
     */
    @Override
    public void upperAlbum(Long albumId) {
        //商品上架
        AlbumInfoIndex albumInfoIndex = new AlbumInfoIndex();
        //获取专辑数据
        CompletableFuture<AlbumInfo> albumInfoCompletableFuture = CompletableFuture.supplyAsync(() -> {
            AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(albumId).getData();
            System.out.println(albumInfo);
            Assert.notNull(albumInfo, "对象不能为空");
            BeanUtil.copyProperties(albumInfo, albumInfoIndex);
            //设置更新时间
            albumInfoIndex.setCreateTime(new Date());
            //封装专辑属性值
            List<AlbumAttributeValue> attributeValueVoList = albumInfo.getAlbumAttributeValueVoList();
            List<AttributeValueIndex> attributeValueIndexList = attributeValueVoList.stream().map(albumAttributeValue -> {
                return BeanUtil.copyProperties(albumAttributeValue, AttributeValueIndex.class);
            }).collect(Collectors.toList());
            albumInfoIndex.setAttributeValueIndexList(attributeValueIndexList);
            return albumInfo;
        }, executor);

        //获取用户信息
        CompletableFuture<Void> userInfoCompletableFuture = albumInfoCompletableFuture.thenAcceptAsync(albumInfo -> {
            Result<UserInfoVo> userInfoVo = userFeignClient.getUserInfoVo(albumInfo.getUserId());
            Assert.notNull(userInfoVo, "主播信息可能不存在，主播编号{}", albumInfo.getUserId());
            albumInfoIndex.setAnnouncerName(userInfoVo.getData().getNickname());
        }, executor);

        //获取三级分类数据
        CompletableFuture<Void> baseCategoryViewCompletableFuture = albumInfoCompletableFuture.thenAcceptAsync(albumInfo -> {
            BaseCategoryView baseCategoryView = albumFeignClient.getCategoryView(albumInfo.getCategory3Id()).getData();
            Assert.notNull(baseCategoryView, "三级分类id不存在{}", albumInfo.getCategory3Id());
            albumInfoIndex.setCategory1Id(baseCategoryView.getCategory1Id());
            albumInfoIndex.setCategory2Id(baseCategoryView.getCategory2Id());
            albumInfoIndex.setCategory3Id(baseCategoryView.getCategory3Id());
        }, executor);
        //获取专辑统计数据
        CompletableFuture<Void> albumStatCompletableFuture = albumInfoCompletableFuture.thenAcceptAsync(albumInfo -> {
//            Result<AlbumStatVo> albumStatVo = albumFeignClient.getAlbumStatVo(albumInfo.getId());

            //暂时使用随机值
            //5.1 随机为专辑产生播放量，订阅量，购买量，评论量 、
            int num1 = RandomUtil.randomInt(1000, 2000);
            int num2 = RandomUtil.randomInt(500, 1000);
            int num3 = RandomUtil.randomInt(200, 400);
            int num4 = RandomUtil.randomInt(100, 200);
            albumInfoIndex.setPlayStatNum(num1);
            albumInfoIndex.setSubscribeStatNum(num2);
            albumInfoIndex.setBuyStatNum(num3);
            albumInfoIndex.setCommentStatNum(num4);

            //5.2 基于统计值计算出专辑得分 为不同统计类型设置不同权重
            BigDecimal bigDecimal1 = new BigDecimal(num4).multiply(new BigDecimal("0.4"));
            BigDecimal bigDecimal2 = new BigDecimal(num3).multiply(new BigDecimal("0.3"));
            BigDecimal bigDecimal3 = new BigDecimal(num2).multiply(new BigDecimal("0.2"));
            BigDecimal bigDecimal4 = new BigDecimal(num1).multiply(new BigDecimal("0.1"));
            BigDecimal hotScore = bigDecimal1.add(bigDecimal2).add(bigDecimal3).add(bigDecimal4);
            //设置专辑热度值
            albumInfoIndex.setHotScore(hotScore.doubleValue());
        }, executor);
        //并行
        albumInfoCompletableFuture.allOf(albumInfoCompletableFuture, userInfoCompletableFuture,
                baseCategoryViewCompletableFuture, albumStatCompletableFuture).join();
        repository.save(albumInfoIndex);

        //关键字自动补全库
        SuggestIndex suggestIndex = new SuggestIndex();
        //设置自动补全主键
        suggestIndex.setId(albumInfoIndex.getId().toString());
        //提词内容
        suggestIndex.setTitle(albumInfoIndex.getAlbumTitle());
        //汉字题词
        suggestIndex.setKeyword(new Completion(new String[]{albumInfoIndex.getAlbumTitle()}));
        //拼音题词
        suggestIndex.setKeywordPinyin(new Completion(new String[]{PinyinUtil.getPinyin(albumInfoIndex.getAlbumTitle())}));
        //拼音首字母
        suggestIndex.setKeywordSequence(new Completion(new String[]{PinyinUtil.getFirstLetter(albumInfoIndex.getAlbumTitle(), "")}));
        //保存提词库
        suggestIndexRepository.save(suggestIndex);
    }

    /**
     * 下架专辑-删除文档
     *
     * @param albumId
     */
    @Override
    public void lowerAlbum(Long albumId) {
        repository.deleteById(albumId);
    }

    /**
     * 专辑检索
     *
     * @param albumIndexQuery
     * @return
     */
    @SneakyThrows
    @Override
    public AlbumSearchResponseVo albumInfo(AlbumIndexQuery albumIndexQuery) {
        //es请求对象
        SearchResponse<AlbumInfoIndex> albumInfoIndexSearch = this.getAlbumInfoIndexSearchResponse(albumIndexQuery);

        //封装对象
        AlbumSearchResponseVo albumSearchResponseVo = getAlbumSearchResponseVo(albumIndexQuery, albumInfoIndexSearch);

        return albumSearchResponseVo;
    }

    @SneakyThrows
    @Override
    public List<Map<String, Object>> channelCategory1Id(Long category1Id) {
        Result<List<BaseCategory3>> clientTopBaseCategory3 = albumFeignClient.findTopBaseCategory3(category1Id);
        List<BaseCategory3> topBaseCategory3 = clientTopBaseCategory3.getData();
        Map<Long, BaseCategory3> baseCategory3Map = topBaseCategory3.stream().collect(Collectors.toMap(BaseCategory3::getId, baseCategory3 -> baseCategory3));
        List<FieldValue> fieldValueList = topBaseCategory3.stream().map(topBaseCategory -> FieldValue.of(topBaseCategory.getId())).collect(Collectors.toList());
        //创建请求对象
        SearchResponse<AlbumInfoIndex> responseDSL = elasticsearchClient.search(s -> s.index(ALBUM_INFO).size(0)
                .query(q -> q.terms(t -> t.field("category3Id").terms(tf -> tf.value(fieldValueList))))
                .size(0)
                .aggregations("category3Agg", agg -> agg
                        .terms(t -> t.field("category3Id"))
                        .aggregations("top6Agg", aggTop6 -> aggTop6.topHits(th -> th.size(6).sort(so -> so.field(f -> f.field("hotScore").order(SortOrder.Asc)))))), AlbumInfoIndex.class);
        //解析数据
        List<Map<String, Object>> listBase3Map = new ArrayList<>();
        List<LongTermsBucket> category3AggBucket = responseDSL.aggregations().get("category3Agg").lterms().buckets().array();
        if (category3AggBucket != null) {
            listBase3Map = category3AggBucket.stream().map(bucket -> {
                Map<String, Object> base3Map = new HashMap<>();
                long key = bucket.key();
                //返回值
                base3Map.put("baseCategory3", baseCategory3Map.get(key));
                List<Hit<JsonData>> top6Agg = bucket.aggregations().get("top6Agg").topHits().hits().hits();
                List<AlbumInfoIndex> albumInfoIndexList = top6Agg.stream().map(top -> top.source().to(AlbumInfoIndex.class)).collect(Collectors.toList());
                base3Map.put("list", albumInfoIndexList);
                return base3Map;
            }).collect(Collectors.toList());
        }
        return listBase3Map;
    }

    @Override
    public List<String> completeSuggest(String keyword) {
        System.out.println(keyword);
        //创建请求对象
        try {
            SearchResponse<SuggestIndex> suggestIndexSearchResponse = elasticsearchClient.search(s -> s.index(SUGGEST).suggest(s1 -> s1
                    .suggesters("mySuggestKeyword", sf -> sf.prefix(keyword).completion(c -> c.field("keyword").size(10).skipDuplicates(true)))
                    .suggesters("mySuggestPinyin", sf -> sf.prefix(keyword).completion(c -> c.field("keywordPinyin").size(10).skipDuplicates(true)))
                    .suggesters("mySuggestSequence", sf -> sf.prefix(keyword).completion(c -> c.field("keywordSequence").size(10).skipDuplicates(true)))), SuggestIndex.class);
            Set<String> hashSet = new HashSet<>();
            hashSet.addAll(this.parseSuggestResult("mySuggestKeyword", suggestIndexSearchResponse));
            hashSet.addAll(this.parseSuggestResult("mySuggestPinyin", suggestIndexSearchResponse));
            hashSet.addAll(this.parseSuggestResult("mySuggestSequence", suggestIndexSearchResponse));
            if (hashSet.size() >= 10) {
                return hashSet.stream().toList();
            }
            //全文查询专辑索引库尝试补全
            SearchResponse<AlbumInfoIndex> search = elasticsearchClient.search(s -> s.query(q -> q.match(m -> m.field("albumTitle").query(keyword))).index(ALBUM_INFO), AlbumInfoIndex.class);
            List<Hit<AlbumInfoIndex>> hits = search.hits().hits();
            if (CollectionUtil.isNotEmpty(hits)) {
                for (Hit<AlbumInfoIndex> hit : hits) {
                    String albumTitle = hit.source().getAlbumTitle();
                    hashSet.add(albumTitle);
                    if (hashSet.size() >= 10) {
                        return hashSet.stream().toList();
                    }
                }
            }
            return hashSet.stream().toList();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private Collection<String> parseSuggestResult(String mySuggestKey, SearchResponse<SuggestIndex> suggestIndexSearch) {
        List<Suggestion<SuggestIndex>> suggestions = suggestIndexSearch.suggest().get(mySuggestKey);
        List<String> list = new ArrayList<>();
        suggestions.stream().forEach(suggestion -> {
            List<CompletionSuggestOption<SuggestIndex>> options = suggestion.completion().options();
            if (CollectionUtil.isNotEmpty(options)) {
                List<String> collect = options.stream().map(option -> option.source().getTitle()).collect(Collectors.toList());
                list.addAll(collect);
            }
        });
        return list;
    }

    @NotNull
    public static AlbumSearchResponseVo getAlbumSearchResponseVo(AlbumIndexQuery albumIndexQuery, SearchResponse<AlbumInfoIndex> albumInfoIndexSearch) {
        HitsMetadata<AlbumInfoIndex> hits = albumInfoIndexSearch.hits();

        // 1. 填充分页信息
        AlbumSearchResponseVo albumSearchResponseVo = new AlbumSearchResponseVo();
        albumSearchResponseVo.setTotal(hits.total().value()); // 总命中数:ml-citation{ref="2" data="citationList"}
        albumSearchResponseVo.setPageSize(albumIndexQuery.getPageSize());  // 分页参数需在 build 中定义:ml-citation{ref="6" data="citationList"}
        albumSearchResponseVo.setPageNo(albumIndexQuery.getPageNo());
        albumSearchResponseVo.setTotalPages(hits.total().value() % albumIndexQuery.getPageSize() == 0 ? hits.total().value() / albumIndexQuery.getPageSize() : hits.total().value() / albumIndexQuery.getPageSize() + 1);

        // 2. 提取文档列表
        List<Hit<AlbumInfoIndex>> albumInfoIndexs = hits.hits();
        if (CollectionUtil.isNotEmpty(albumInfoIndexs)) {
            List<AlbumInfoIndexVo> albumInfoIndexVos = albumInfoIndexs.stream().map(hit -> {

                AlbumInfoIndex albumInfoIndex = hit.source(); // 直接获取映射后的对象
                AlbumInfoIndexVo albumInfoIndexVo = BeanUtil.copyProperties(albumInfoIndex, AlbumInfoIndexVo.class);
                List<String> albumTitle = hit.highlight().get("albumTitle");
                if (CollectionUtil.isNotEmpty(albumTitle)) {
                    albumInfoIndexVo.setAlbumTitle(albumTitle.get(0));
                }

                return albumInfoIndexVo;
            }).collect(Collectors.toList());
            albumSearchResponseVo.setList(albumInfoIndexVos);
        }
        System.out.println(albumSearchResponseVo);
        return albumSearchResponseVo;
    }

    public SearchResponse<AlbumInfoIndex> getAlbumInfoIndexSearchResponse(AlbumIndexQuery albumIndexQuery) throws IOException {
        System.out.println(albumIndexQuery);
        //创建请求对象
        SearchRequest.Builder builder = new SearchRequest.Builder();
        //指定索引库
        builder.index("albuminfo");
        //多条件 bool创建
        BoolQuery.Builder boolBuilder = new BoolQuery.Builder();
        //索引值
        String keyword = albumIndexQuery.getKeyword();

        if (StringUtils.isNotEmpty(keyword)) {
            BoolQuery.Builder keywordboolBuilder = new BoolQuery.Builder();
            keywordboolBuilder.should(s -> s.match(m -> m.field("albumTitle").query(keyword)));
            keywordboolBuilder.should(s -> s.match(m -> m.field("albumIntro").query(keyword)));
            keywordboolBuilder.should(s -> s.term(t -> t.field("announcerName").value(keyword)));

            boolBuilder.must(keywordboolBuilder.build()._toQuery());
        }
        //分级分类
        Long category1Id = albumIndexQuery.getCategory1Id();
        if (category1Id != null) {
            boolBuilder.filter(f -> f.term(t -> t.field("category1Id").value(category1Id)));
        }
        if (albumIndexQuery.getCategory2Id() != null) {
            boolBuilder.filter(f -> f.term(t -> t.field("category2Id").value(albumIndexQuery.getCategory2Id())));
        }
        if (albumIndexQuery.getCategory3Id() != null) {
            boolBuilder.filter(f -> f.term(t -> t.field("category3Id").value(albumIndexQuery.getCategory3Id())));
        }
        List<String> attributeList = albumIndexQuery.getAttributeList();
        if (CollectionUtil.isNotEmpty(attributeList)) {
            attributeList.stream().forEach(attribute -> {
                String[] split = attribute.split(":");
                if (split != null && split.length == 2) {
                    boolBuilder.filter(f -> f.nested(n ->
                            n.path("attributeValueIndexList")
                                    .query(q -> q.bool(b ->
                                            b.filter(f1 -> f1.term(t -> t.field("attributeValueIndexList.attributeId").value(split[0])))
                                                    .filter(f2 -> f2.term(t -> t.field("attributeValueIndexList.valueId").value(split[1])))))));
                }
            });
        }
        //添加条件
        builder.query(boolBuilder.build()._toQuery());
        //索引页 index
        int startIndex = (albumIndexQuery.getPageNo() - 1) * albumIndexQuery.getPageSize();
        builder.from(startIndex);
        builder.size(albumIndexQuery.getPageSize());
        //高亮
        if (StringUtils.isNotEmpty(keyword)) {
            builder.highlight(h -> h.fields("albumTitle", fi -> fi.preTags("<font color='red'>").postTags("</font>")));
        }
        //排序
        String order = albumIndexQuery.getOrder();
        if (StringUtils.isNotEmpty(order)) {
            String[] split = order.split(":");
            if (split != null && split.length == 2) {
                String field = "";
                switch (split[0]) {
                    case "1":
                        field = "hotScore";
                        break;
                    case "2":
                        field = "playStatNum";
                        break;
                    case "3":
                        field = "createTime";
                        break;
                }
                String esfield = field;
                builder.sort(s -> s.field(f -> f.order(split[1].equals("desc") ? SortOrder.Desc : SortOrder.Asc).field(esfield)));
            }
        }
        //过滤
        builder.source(s -> s.filter(f -> f.excludes(Arrays.asList("category1Id",
                "category2Id",
                "category3Id",
                "hotScore",
                "attributeValueIndexList"))));
        //返回请求对象
        SearchRequest searchRequest = builder.build();
        System.out.println(searchRequest.toString());
        SearchResponse<AlbumInfoIndex> albumInfoIndexSearch = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);
        return albumInfoIndexSearch;
    }
}
